package com.cslc.mp.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import net.sf.json.JSONObject;

import org.apache.log4j.Logger;

import com.cslc.mp.bean.AccessTokenInfo;
import com.cslc.mp.bean.JsApiTicketInfo;
import com.cslc.mp.constant.MpApi;
import com.cslc.mp.constant.MpInfo;
import com.cslc.mp.exception.WeChatException;
import com.cslc.mp.exception.WeChatReturnCode;

/**
 * 工具类
 * 
 * @author keliangliang
 * 
 */
public class WeChatUtil {
	private static Logger logger = Logger.getLogger(WeChatUtil.class.getName());
	// 获取access_token的接口地址（GET） 限200（次/天），保存在内存中
	static AccessTokenInfo accessToken = null;

	// 有效期7200秒，开发者必须在自己的服务全局缓存jsapi_ticket
	static JsApiTicketInfo jsapi_ticket = null;

	/**
	 * 获取jsapi_ticket
	 * @return
	 */
	public static JsApiTicketInfo getJsApiTicket() {
		// 获取当前系统时间
		long now = System.currentTimeMillis() / 1000;
		// 首次获取
		if (jsapi_ticket == null) {
			jsapi_ticket = new JsApiTicketInfo();
			createJsApiTicket(now);
		} else {
			// 提前30s重新获取token
			if (jsapi_ticket.getTimestamp() + (jsapi_ticket.getExpiresIn() - 30) < now) {
				createJsApiTicket(now);
			}
		}
		return jsapi_ticket;
	}
	
	/**
	 * 通过access_token创建新的jsapi_token
	 * @param
	 */
	static void createJsApiTicket(long now) {
		String access_token = WeChatUtil.getAccessToken().getToken();
		String requestUrl = String.format(MpApi.GET_JSAPI_TICKET.getUrl(), access_token);
		String requestMethod = MpApi.GET_JSAPI_TICKET.getMethod();
		JSONObject jsonObject = httpRequest(requestUrl, requestMethod, null);
		logger.debug("重新获取jsapi_ticket：" + jsonObject);
		try {
			// 判断请求是否成功，否则抛出异常
			WeChatUtil.isSuccess(jsonObject);
			jsapi_ticket.setTicket(jsonObject.getString("ticket"));
			jsapi_ticket.setExpiresIn(jsonObject.getInt("expires_in"));
			jsapi_ticket.setTimestamp(now);
		} catch (WeChatException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		
	}

	/**
	 * 判断是否在有效期内获取access_token
	 * 
	 * @param appid
	 *            凭证
	 * @param appsecret
	 *            密钥
	 * @return
	 */
	public static AccessTokenInfo getAccessToken() {
		// 获取当前系统时间
		long now = System.currentTimeMillis() / 1000;
		// 首次获取
		if (accessToken == null) {
			accessToken = new AccessTokenInfo();
			createAccessToken(now);
		} else {
			// 提前30s重新获取token
			if (accessToken.getTimeStamp() + (accessToken.getExpiresIn() - 30) < now) {
				createAccessToken(now);
			}
		}
		return accessToken;

	}

	/**
	 * 通过get请求返回accesstoken
	 * 
	 * @param now
	 *            当前时间（秒）
	 */
	public static void createAccessToken(long now) {
		String requestUrl = String.format(MpApi.GET_TOKEN.getUrl(),
				MpInfo.APP_ID, MpInfo.APP_SECRET);
		String requestMethod = MpApi.GET_TOKEN.getMethod();
		JSONObject jsonObject = httpRequest(requestUrl, requestMethod, null);
		logger.debug("重新获取AccessToken：" + jsonObject);
		try {
			// 判断请求是否成功，否则抛出异常
			WeChatUtil.isSuccess(jsonObject);
			accessToken.setToken(jsonObject.getString("access_token"));
			accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
			accessToken.setTimeStamp(now);
		} catch (WeChatException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}

	/**
	 * 判断是否请求成功
	 * 
	 * @param resultJson
	 * @throws WeChatException
	 */
	public static void isSuccess(JSONObject resultJson) throws WeChatException {

		Integer errCode = null;
		// 如果http请求的返回json数据包为空，抛出异常
		if (resultJson == null)
			throw new WeChatException("http(s)请求失败！");
		// 如果返回值不空且包含异常码，则取出异常码的值（此判断防止出现NullPointerException）
		if (resultJson != null && resultJson.containsKey("errcode"))
			errCode = resultJson.getInt("errcode");
		// 如果异常码不为0，即返回异常，则抛出异常说明
		if (errCode != null && errCode != 0) {
			String errMsg = WeChatReturnCode.getMsg(errCode);
			if (errMsg.equals("")) {
				errMsg = resultJson.getString("errsg");
			}
			throw new WeChatException("异常码:" + errCode + ";异常说明:" + errMsg);
		}
	}

	/**
	 * 构造http请求，访问微信接口，并将返回字符串转成json对象
	 * 
	 * @param requestUrl
	 * @param requestMethod
	 * @param outputStr
	 * @return
	 * @throws IOException
	 */
	public static JSONObject httpRequest(String requestUrl,
			String requestMethod, String outputStr) {
		JSONObject jsonObject = null;
		StringBuilder sBuilder = new StringBuilder();
		try {
			URL url = new URL(requestUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);

			// 设置请求方式（GET/POST）
			conn.setRequestMethod(requestMethod);
			// 当有数据需要提交时
			if (null != outputStr) {
				OutputStream outputStream = conn.getOutputStream();
				// 注意编码格式，防止中文乱码
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符串
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(
					inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(
					inputStreamReader);

			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				sBuilder.append(str);
			}
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			conn.disconnect();

			jsonObject = JSONObject.fromObject(sBuilder.toString());
		} catch (MalformedURLException e) {
			logger.error(e.getMessage());
		} catch (IOException e) {
			logger.error(e.getMessage());
		}
		return jsonObject;
	}

	/**
	 * 构造POST请求报文，实现文件（image，voice，video，file）上传
	 * 
	 * @param requestUrl
	 * @param mediaFileUrl
	 * @return
	 * @throws IOException
	 */
	public static JSONObject uploadMedia(String requestUrl, String mediaFileUrl)
			throws IOException {

		JSONObject jsonObject = null;
		// 换行符
		final String NEWLINE = "\r\n";
		final String BOUNDARY_PREFIX = "--";
		// 定义数据分隔符
		final String BOUNDARY = "------------7da2e536604c8";
		URL url = new URL(requestUrl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		// 设置为POST请求
		conn.setRequestMethod("POST");
		// 发送POST请求必须设置如下两行
		conn.setDoOutput(true);
		conn.setDoInput(true);
		conn.setUseCaches(false);
		// 设置请求头参数
		conn.setRequestProperty("Connection", "Keep-Alive");
		conn.setRequestProperty("Charsert", "UTF-8");
		conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="
				+ BOUNDARY);
		OutputStream out = conn.getOutputStream();

		// 上传文件
		File file = new File(mediaFileUrl);
		StringBuilder sb = new StringBuilder();
		sb.append(BOUNDARY_PREFIX);
		sb.append(BOUNDARY);
		sb.append(NEWLINE);
		// 文件参数，photo参数名可以随意修改
		sb.append("Content-Disposition: form-data;name=\"media\";filename=\""
				+ mediaFileUrl + "\"" + NEWLINE);
		sb.append("Content-Type:application/octet-stream");
		// 参数头设置完成以后需要两个换行，然后才是参数内容
		sb.append(NEWLINE);
		sb.append(NEWLINE);
		// 将参数头的数据写入到输出流中
		out.write(sb.toString().getBytes());
		// 数据输入流,用于读取文件数据
		DataInputStream in = new DataInputStream(new FileInputStream(file));
		byte[] bufferOut = new byte[1024];
		int bytes = 0;
		// 每次读1KB数据,并且将文件数据写入到输出流中
		while ((bytes = in.read(bufferOut)) != -1) {
			out.write(bufferOut, 0, bytes);
		}
		// 最后添加换行
		out.write(NEWLINE.getBytes());
		in.close();
		// 定义最后数据分隔线，即--加上BOUNDARY再加上--。
		byte[] end_data = (NEWLINE + BOUNDARY_PREFIX + BOUNDARY
				+ BOUNDARY_PREFIX + NEWLINE).getBytes();
		// 写上结尾标识
		out.write(end_data);
		out.flush();
		out.close();

		// 将返回的输入流转换成字符串
		// 将返回的输入流转换成字符串

		InputStream inputStream = conn.getInputStream();
		InputStreamReader inputStreamReader = new InputStreamReader(
				inputStream, "utf-8");
		BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
		StringBuilder sBuilder = new StringBuilder();
		String str = null;
		while ((str = bufferedReader.readLine()) != null) {
			sBuilder.append(str);
		}
		bufferedReader.close();
		inputStreamReader.close();
		// 释放资源
		inputStream.close();
		inputStream = null;
		conn.disconnect();
		// 将返回的字符串转换成json对象
		jsonObject = JSONObject.fromObject(sBuilder.toString());

		return jsonObject;
	}

	/**
	 * 从本地读取文件
	 * 
	 * @param fileName
	 */
	public static String readFile(String fileName) {
		try {
			InputStreamReader isr = new InputStreamReader(new FileInputStream(
					fileName), "UTF-8");
			BufferedReader reader = new BufferedReader(isr);
			StringBuilder sBuilder = new StringBuilder();
			String content;
			while ((content = reader.readLine()) != null) {
				sBuilder.append(content);
			}
			return sBuilder.toString();
		} catch (FileNotFoundException e) {
			logger.error(e.getMessage());
			return null;
		} catch (IOException e) {
			logger.error(e.getMessage());
			return null;
		}
	}
	
	/**
	 * 从微信服务器下载多媒体文件（临时或永久素材）
	 * @param requestUrl
	 * @param mediaId
	 * @param savePath
	 * @return
	 */
	public static String getMedia(String requestUrl, String mediaId, String savePath) {
		String filePath = null;
		URL url;
		try {
			url = new URL(requestUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoInput(true);
			conn.setRequestMethod("GET");
			
			if(!savePath.endsWith("/")) {
				savePath += "/";
			}
			//根据内容类型获取扩展名
			String fileExt = ".jpg";
			filePath = savePath + mediaId + fileExt;
			
			BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
			FileOutputStream fos = new FileOutputStream(new File(filePath));
			byte[] buf = new byte[8096];
			int size = 0;
			while((size = bis.read(buf)) != -1)
				fos.write(buf, 0, size);
			fos.close();
			bis.close();
			
			conn.disconnect();
			logger.info("下载媒体文件成功， filePath=" + filePath);
		} catch (Exception e) {
			filePath = null;
			logger.error("下载媒体文件失败： {}", e);
		}
		return filePath;
	}
}
